package nirvana.client.zk;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
/**
 * 管理ZooKeeper连接池
 * @author zhanghongyu
 */
public class ZooKeeperPool{

	public static ZooKeeper zooKeeper;
	public static CountDownLatch latch = null;
	
	/**
	 *  Session 连接：
	 * 		  Zookeeper客户端和服务端维持一个长连接，每隔几秒向服务端发送一个心跳，服务端返回客户端一个响应。
	 * 	            这就是一个Session连接，拥有全局唯一的session id。
	 *  注意事项：静态方法加锁，其实锁的是方法所在的类所对应的Class对象
	 * @return zooKeeper
	 */
	public static ZooKeeper getZooKeeperConnection() throws ZooKeeperException{
		if(CheckUtil.isNull(zooKeeper)){
			synchronized (ZooKeeperPool.class) {
				if(CheckUtil.isNull(zooKeeper)){
					latch = new CountDownLatch(1);
					zooKeeper = creatZooKeepConnection();
				}
			}
		}
		return zooKeeper;
	}
	
	/**
	 * 创建连接
	 * @return zooKeeper
	 * @throws ZooKeeperException
	 */
	private static ZooKeeper creatZooKeepConnection() throws ZooKeeperException{
		try {
			zooKeeper = new ZooKeeper(ZooKeeperConstant.ZOOKEEPER_HOSTS,
					                  ZooKeeperConstant.ZOOKEEPER_SESSION_OUT, 
					                  new SeesionWatcher());
		} catch (IOException e) {
			throw new ZooKeeperException(e);
		}
		return zooKeeper;
	}
	/**
	 * 关闭连接：不建议手动关闭，客户端和服务端维持的是一个常连接
	 *              如果是客户端当机了，服务端也会尝试拿全局的SessionID去重新连接
	 * @throws ZooKeeperException
	 */
	public static void closeZooKeeperConnection() throws ZooKeeperException{
		if(CheckUtil.isNotNull(zooKeeper)){
			try {
				zooKeeper.close();
			} catch (InterruptedException e) {
				throw new ZooKeeperException(e);
			}
		}
	}
	
	static class SeesionWatcher implements Watcher{
		@Override
		public void process(WatchedEvent event) {
			if(event.getState() == KeeperState.SyncConnected){
				if(CheckUtil.isNotNull(latch)){
					latch.countDown();
				}
			}
		}
	}
}
